"use strict";

const xlsx = require('xlsx');
const crypot = require('crypto');
const path = require('path');
const fs = require('fs');
const util = require('util');
const process = require('process');
let writer = require('../utils/generate-code');

let excelPath = /* process.env.GAMEEXCELPATH || */ path.resolve(__dirname, '../excel')
const outPath = path.resolve(__dirname, '../../assets/data');

const tsutils = new writer();
const namesutls = new writer();

const alldata = {};

//客户端专有字段、服务器专有字段 c|s
const target /* "client"|"server" */ = "c";

!fs.existsSync(outPath) && fs.mkdirSync(outPath);

module.exports = generateAll;

function generateAll() {

    let dir = fs.readdirSync(excelPath);
    console.log("读取目录=======>", dir);
    let tasks = [];

    tsutils.reset();
    namesutls.reset();

    tsutils.writeln(`import { BaseData } from "./DataCustom"`).writeln()

    namesutls.writeln('/** 自动生成代码 */');
    namesutls.writeln('export enum DataEnum{').incIndent();

    for (let elm of dir) {
        if (path.extname(elm) !== ".xlsx" || elm.slice(0, 2) === "~$") continue;

        let ext = path.extname(elm);
        let dataName = path.basename(elm, ext);

        namesutls.writeln('{0} = "{1}",', dataName, dataName);

        // console.log("start build->", dataName);
        tasks.push(generateJson(path.join(excelPath, elm)));
    }
    namesutls.endBlock();

    return Promise.all([...tasks, mergeJson(), saveNames(), saveData()]);
}

//生成js
function generateJson(p) {
    let ext = path.extname(p);
    let dataName = path.basename(p, ext);
    // namesutls.writeln('{0} = "{1}",', dataName, dataName);
    console.log("start build->", dataName);


    let excel = xlsx.readFile(p, { type: "binary", cellDates: true, cellText: false });
    let sheet = excel.Sheets[excel.SheetNames[0]];

    let range = safe_decode_range(sheet['!ref']);
    let startC = range.s.c = 1, startR = range.s.r = 0;
    let endC = range.e.c, endR = range.e.r;

    //字段类型，字段说明，有效字段列，字段名字
    let types = [], des = [], rList = [], files = [];

    //字段类型与说明
    for (let c = startC; c <= endC; c++) {
        let temp = encode_col(c);

        let t = sheet[temp + encode_row(0)];    //字段类型
        let d = sheet[temp + encode_row(1)];    //字段说明
        let n = sheet[temp + encode_row(2)];    //字段名字

        if (n && n.v && n.v !== "") {

            let _nam = n.v.split("_");
            if (target === "c" || target === "client") {
                if (n.v.endsWith("_s")) continue;
            }

            if (target === "s" || target === "server") {
                if (n.v.endsWith("_c")) continue;
            }

            let desV = d && d.v ? d.v : "";
            let type = t && t.v ? t.v : "string";
            types.push(type);
            des.push(desV);
            rList.push(temp);
            files.push(_nam[0]);
        }
    }

    let jsonObj = [files];

    console.log('行数 = %d , 列数 = %d', endR, endC, "\n");

    for (let r = 3; r <= endR; ++r) {
        let datas = [], isempty = true;
        for (let i = 0; i < rList.length; i++) {
            let temp = sheet[rList[i] + encode_row(r)];

            if (isempty && temp && temp.v) isempty = false;

            datas[i] = encode_value(temp, types[i]);
        }
        !isempty && jsonObj.push(datas);
    }
    sheet_to_js(dataName, files, types, des);

    alldata[dataName] = jsonObj;

    return util.promisify(fs.writeFile)(path.join(outPath, dataName + ".json"), JSON.stringify(jsonObj, null, 4));
}

//生成ts文件
function sheet_to_js(name, files, types, des) {
    let formatNmae = name.slice(0, 1).toUpperCase() + name.slice(1);

    tsutils.writeln();
    tsutils.writeln('/** 自动生成文件 */').writeln();
    tsutils.writeContinue(`export class {0} extends BaseData{`, formatNmae).writeln();
    tsutils.incIndent();
    tsutils.writeln('/** 表文件名 */');
    tsutils.writeln(`static readonly dataName = "{0}";`, name).writeln();

    for (let i = 0; i < files.length; i++) {
        tsutils.writeln(`/** ${des[i]} */`);
        tsutils.writeln(`${files[i]}: ${getTsType(types[i])};`).writeln();
    }

    tsutils.endBlock();
}

function saveNames() {
    return namesutls.save(path.resolve(__dirname, "../../src/datavo/DataEnum.ts"));
}

function saveData() {
    return tsutils.save(path.resolve(__dirname, "../../src/datavo/data.ts"));
}

//合并json
function mergeJson() {
    let obj = {};
    // let dataPath = outPath;
    // let dirlist = fs.readdirSync(dataPath);

    let keys = Object.keys(alldata);
    keys.sort();

    for (let key of keys) {
        obj[key] = alldata[key];
    }

    return new Promise((resolve, reject) => {
        fs.writeFile(path.join(outPath, "alldata.json"), JSON.stringify(obj), { encoding: "utf-8" }, e => {

            if (e) return void reject(e);

            resolve();
        })

    });

    // return new Promise((resolve, reject) => {
    //     let loadNum = 0, len = dirlist.length;

    //     for (let dir of dirlist) {
    //         if (path.extname(dir) !== '.json' || dir === 'alldata.json') {
    //             --len
    //             continue;
    //         }

    //         fs.readFile(path.join(dataPath, dir), { encoding: 'utf8' }, (err, data) => {

    //             if (err) return void reject(err);

    //             obj[path.basename(dir, '.json')] = JSON.parse(data);

    //             if (++loadNum !== len) return;

    //             fs.writeFile(path.join(dataPath, "alldata.json"), JSON.stringify(obj), { encoding: "utf-8" }, e => {

    //                 if (e) return void reject(e);

    //                 // copy(dataPath, path.resolve(__dirname, '../bin/data'));
    //                 resolve();
    //             })
    //         })
    //     }
    // })

}

function copy(sd, td) {
    // 读取目录下的文件，返回文件名及文件类型{name: 'xxx.txt, [Symbol(type)]: 1 }
    const sourceFile = fs.readdirSync(sd, { withFileTypes: true })
    for (const file of sourceFile) {
        // 源文件 地址+文件名
        const srcFile = path.resolve(sd, file.name)
        // 目标文件
        const tagFile = path.resolve(td, file.name)
        // 文件是目录且未创建
        if (file.isDirectory() && !fs.existsSync(tagFile)) {
            fs.mkdirSync(tagFile, err => console.log(err))
            copy(srcFile, tagFile)
        } else if (file.isDirectory() && fs.existsSync(tagFile)) {
            // 文件时目录且已存在
            copy(srcFile, tagFile)
        }
        !file.isDirectory() && fs.copyFileSync(srcFile, tagFile, fs.constants.COPYFILE_FICLONE)
    }
}



//字段类型处理静态处理
function encode_value(v, t) {
    switch (t) {
        case "int": {
            if (!v || !v.v) return 0;

            let n = Number(v.v);
            return isNaN(n) ? 0 : n;
        }

        case "date": {//TODO 返回解析后的时间戳
            return Number(v.v);
        }

        case "kv": {//键值对列表
            let values = v.v.split("|");
            if (values.length < 2) {
                let temp = values[0].split(":");
                return { key: temp[0], value: temp[1] }
            }
            let temps = []
            for (let elm of values) {
                let temp = elm.split(":");
                temps.push({ key: temp[0], value: temp[1] });
            }
            return temps;
        }

        default: {
            if (!v || !v.v) return "";

            return String(v.v);
        }
    }
}


function getTsType(t) {
    switch (t) {
        case "int":
            return "number";
        default:
            return "string";
    }
}

function safe_decode_range(range/*:string*/)/*:Range*/ {
    var o = { s: { c: 0, r: 0 }, e: { c: 0, r: 0 } };
    var idx = 0, i = 0, cc = 0;
    var len = range.length;
    for (idx = 0; i < len; ++i) {
        if ((cc = range.charCodeAt(i) - 64) < 1 || cc > 26) break;
        idx = 26 * idx + cc;
    }
    o.s.c = --idx;

    for (idx = 0; i < len; ++i) {
        if ((cc = range.charCodeAt(i) - 48) < 0 || cc > 9) break;
        idx = 10 * idx + cc;
    }
    o.s.r = --idx;

    if (i === len || cc != 10) {
        o.e.c = o.s.c; o.e.r = o.s.r;
        return o;
    }
    ++i;

    for (idx = 0; i != len; ++i) {
        if ((cc = range.charCodeAt(i) - 64) < 1 || cc > 26) break;
        idx = 26 * idx + cc;
    }
    o.e.c = --idx;

    for (idx = 0; i != len; ++i) {
        if ((cc = range.charCodeAt(i) - 48) < 0 || cc > 9) break;
        idx = 10 * idx + cc;
    }
    o.e.r = --idx;
    return o;
}

function encode_col(col/*:number*/)/*:string*/ {
    if (col < 0) throw new Error("invalid column " + col); var s = "";
    for (++col; col; col = Math.floor((col - 1) / 26)) {
        s = String.fromCharCode(((col - 1) % 26) + 65) + s; return s;
    }
}

function encode_row(row/*:number*/)/*:string*/ {
    return "" + (row + 1);
}
